1 module hip.util.variant; 2 3 enum Type : ubyte 4 { 5 undefined, 6 void_, 7 object, 8 string_, 9 bool_, 10 //Signed 11 byte_, 12 short_, 13 int_, 14 long_, 15 //Unsigned 16 ubyte_, 17 ushort_, 18 uint_, 19 ulong_, 20 //Floating 21 float_, 22 double_, 23 ///Untested 24 real_ 25 } 26 27 private union TypeUnion 28 { 29 string string_; 30 void* void_; 31 Object object; 32 bool bool_; 33 //Signed 34 byte byte_; 35 short short_; 36 int int_; 37 long long_; 38 //Unsigned 39 ubyte ubyte_; 40 ushort ushort_; 41 uint uint_; 42 ulong ulong_; 43 //Floating 44 float float_; 45 double double_; 46 ///Untested 47 real real_; 48 } 49 50 pragma(inline, true) 51 pure Type getType(T)() nothrow @nogc @safe 52 { 53 with(Type) 54 { 55 static if(is(T == string)) 56 return string_; 57 else static if(is(T == void*)) 58 return void_; 59 else static if(is(T : Object)) 60 return object; 61 else static if(is(T == bool)) 62 return bool_; 63 else static if(is(T == byte)) 64 return byte_; 65 else static if(is(T == short)) 66 return short_; 67 else static if(is(T == int)) 68 return int_; 69 else static if(is(T == long)) 70 return long_; 71 else static if(is(T == ubyte)) 72 return ubyte_; 73 else static if(is(T == ushort)) 74 return ushort_; 75 else static if(is(T == uint)) 76 return uint_; 77 else static if(is(T == ulong)) 78 return ulong_; 79 else static if(is(T == float)) 80 return float_; 81 else static if(is(T == double)) 82 return double_; 83 else static if(is(T == real)) 84 return real_; 85 else static assert(false, "Unimplemented for type "~T.stringof); 86 } 87 } 88 89 /** 90 * Use that when you want to hold arbitrary defined type (which usually must be converted) 91 * By using variant, your data will be converted only once and after that, it will be runtime 92 * type strict. 93 */ 94 struct Variant 95 { 96 Type type = Type.undefined; 97 TypeUnion data = void; 98 99 string getTypeName() const @nogc 100 { 101 switch(type) 102 { 103 static foreach(mem; __traits(allMembers, Type)) 104 { 105 case __traits(getMember, Type, mem): 106 return mem[0..$-1]; 107 } 108 default: 109 return "undefined"; 110 } 111 } 112 113 T get(T)() const 114 { 115 if(getType!T != type) 116 { 117 import hip.util.string; 118 SmallString s = SmallString("Tried to get a mismatching type: ", T.stringof, "(expected ", getTypeName, ")"); 119 throw new Exception(s.toString); 120 } 121 return *cast(T*)(cast(void*)&data); 122 } 123 T set(T)(T value) 124 { 125 if(type == Type.undefined) 126 this.type = getType!T; 127 else if(type != getType!T) 128 throw new Exception("Tried to get a mismatching type: "~T.stringof~" (expected "~getTypeName~")"); 129 data = *cast(TypeUnion)(cast(void*)&data); 130 return value; 131 } 132 133 T opCast(T)() const 134 { 135 return get!T; 136 } 137 bool opBinary(string op : "in")(const Type t) const 138 { 139 return type == t; 140 } 141 alias opBinaryRight = opBinary; 142 143 static Variant make(string data) 144 { 145 return Variant(Type.string_, cast(TypeUnion)data); 146 } 147 148 static Variant make(T)(T data) if(!is(T == string)) 149 { 150 Variant v = void; 151 v.type = getType!T; 152 v.data = *cast(TypeUnion*)(cast(void*)&data); 153 return v; 154 } 155 156 static Variant make(T)(string data) 157 { 158 import hip.util.conv:to; 159 return make!T(to!T(data)); 160 } 161 }